
#define NDEBUG

#ifdef MOBILE
/*
 * IPhone /mobile version built on the non-official branch of the iphone-dev toolchain.
 * OpenGLES for the IPHONE is new to me but I did find good documentation here:
 *    http://www.typhoonlabs.com/tutorials/gles/Tutorial3.pdf
 * The original scaffolding was Andrew Willmotts gltest.cpp for the iphone.
 */
#include <OpenGLES/gl.h>
#include <OpenGLES/egl.h>
#include <CoreGraphics/CGDirectDisplay.h>
#include <stdio.h>
#include <unistd.h>

#else

/* Full desktop OpenGL version
 */
#include <GL/glut.h>	// Header File For The GLUT Library 
#include <GL/gl.h>	// Header File For The OpenGL32 Library
#include <GL/glu.h>	// Header File For The GLu32 Library
#include <unistd.h>	 // needed to sleep
#include <math.h>
#include <stdio.h>
#include <malloc.h>

#endif

/////////////////////////////////////////////////////////////////////////////////
// miscellaneous declarations
////////////////////////////////////////////////////////////////////////////////

#include "earth.h"
#include "globe.h"
#include "arcball.c"

#define WIDTH 512
#define HEIGHT 512
#define ESCAPE 27

GLfloat glcolors[] = {
	1,1,1,1,
	1,1,1,1,
	1,1,1,1,
};

GLfloat glvertices[] = {
	0,0,0,
	0,0,0,
	0,0,0,
	0,0,0
};

GLfloat gltextures[] = {
	0,0,
	0,0,
	0,0,
	0,0,
};

int	is_flat=0;
float	surface=1;
float	zoom=0;
float	longitude=0;
float	latitude=0;
int	pw=512;
int	ph=512;
int	tilepw=256;
int	tileph=128;
int	wireframe=0;
float*	world_matrix = 0;
int	initialized = 0;
GLuint	texture[1];
extern GLuint texture_earth[];

///////////////////////////////////////////////////////////////////////////////
//
// print a sphere with some specific constraints
//    + print any tiled portion on demand
//    + apply curvature if desired
//    + associate with wms server
//    + allow control over tile sizes
//
// give it a point on the surface { as a longitude, latitude, zoom }
// and it makes a visible zone around that surface based on your degree of zoom
// has consideration for tiling strategies; such as square tiles etcetera.
//
// this routine could fetch too many tiles at the poles so i will hardcode them
// 
////////////////////////////////////////////////////////////////////////////////

void globe_print() {

	// size of world
	float wwidth = 360.0;
	float wheight = 180.0;

	// how many tiles would we like to use to tile the world?
	int tiles = (int)pow(2,zoom);

	 // how big are the tiles in world space?
	float tilegw = wwidth/tiles;
	float tilegh = wheight/tiles;

	// get a positive tile index
	int tilex = (int)round((longitude+(wwidth/2))/tilegw);
	int tiley = (int)round((latitude+(wheight/2))/tilegh);

	// pick a patch that defines the visible mesh extent
	// at the chosen point... can wraparound axes.
	int LT = (tilex-(int)round(pw/tilepw/2));
	int RT = (tilex+(int)round(pw/tilepw/2));
	int TP = (tiley-(int)round(ph/tileph/2));
	int BT = (tiley+(int)round(ph/tileph/2));

	// hack: due to wrap at low zoom just show the whole planet as one tile
	if(zoom < 4) {
		tiles = 1;
		tilex = 0;
		tiley = 0;
		LT = 0;
		RT = 1;
		TP = 0;
		BT = 1;
	}

	// decide on an integer subtiling regimen to provide
	// a nice sense of curvature for the mesh
	int st = (is_flat || tiles > 16 ) ? 1 : ((int)ceil(16/tiles));

	// make vertices for the mesh; series of horizontal strings of vertices
	int i;
	int j;
	float x;
	float y;
	float z = 0;
	float t;
	float rad1;
	float rad2;
	float *v;
	int vr_length = (RT-LT)*st+1;
	int vrs_total = (BT-TP)*st+1;
	int vertices_length = vr_length * vrs_total * 3;

	GLfloat vertices[vertices_length];
	// float m = 0;
	// int test = 0;
	// if(test){glBegin(GL_POINTS);}
	for(j=0;j<vrs_total;j++) {// visit vertical range of st EDGES
		if(!is_flat) {
			// fractional degree along the arc of latitude
			rad1 = 1.0f*(j+TP)/(tiles*st);
			// might as well calculate y now
			y = -surface*cos(rad1*3.14159265f);
			// convenience
			t = surface*sin(rad1*3.14159265f);
		}
		v = 0;	// reset vertex to deal with poles
		for(i=0;i<vr_length;i++) {// visit horiz range of st EDGES!
			if(is_flat) {
			x = surface/wwidth*((i+LT)/st*tilegw-(wwidth/2));
			y = surface/wheight*((j+TP)/st*tilegh-(wheight/2));
			// y = y * tileratio;
			} else {
				rad2 = 2.0f*(i+LT)/(tiles*st);
				// fractional degree along arc of longitude
				x = -t*sin(rad2*3.14159265f);
				z = t*cos(rad2*3.14159265f);
			}
			// optional optimization - only one vertex at pole
			//if(is_flat==0 && (rad1==0||rad1>=1 && v!=null) {
			//  TODO should really enable this to prevent seams
			// but it requires tracking vrs...
			//} else
			{
#ifdef MOBILE
				vertices[(j*vr_length+i)*3+0] = x;
				vertices[(j*vr_length+i)*3+1] = y;
				vertices[(j*vr_length+i)*3+2] = z;
#else
				vertices[(j*vr_length+i)*3+0] = x;
				vertices[(j*vr_length+i)*3+1] = -y;
				vertices[(j*vr_length+i)*3+2] = -z;
#endif
				//if(test){
				//glColor3f(m*2,1.0f,1.0f);
				//glVertex3f(x+m,y+m,z+m); m = m + 0.001;
				//}
			}
		}
	}
	//if(test){glEnd();return;}

	/*
	// make textures
	var key:String;
	var m:BitmapFileMaterial;
	var materials:MaterialsList = new MaterialsList();
	var material_row:Array;
	var material_rows:Array = new Array();
	for(j=TP;j<BT;j++) {		// visit vertical major tiles range
		material_row = new Array();
		material_rows.push(material_row);
		for(i=LT;i<RT;i++) {	// visit horizontal major tiles range
			key = wmsurl
				+ "&WIDTH="+tilepw
				+ "&HEIGHT="+tileph
				+ "&BBOX="
				+ (i*tilegw-(wwidth/2))	// right prime meridian
				+ ","
				+ (j*tilegh-(wheight/2))// south of north pole
				+ ","
				+ (i*tilegw+tilegw-(wwidth/2))	// width
				+ ","
				+ (j*tilegh+tilegh-(wheight/2))	// height
				;
			m = new BitmapFileMaterial(key);
			m.doubleSided = 0;
			m.smooth = 1;
			materials.addMaterial(m,key);
			material_row.push(key);
		}
	}
	*/

	// constant: make an idealized uv subdivision of a tile
	float uv_rows[(st+1)*(st+1)*2];
	for(j=0;j<=st;j++) {	// visit vertical subrange EDGES
		for(i=0;i<=st;i++) {// visit horizontal subrange EDGES
			uv_rows[(j*st+j+i)*2+0]=1.0f*i/st;
			uv_rows[(j*st+j+i)*2+1]=1.0f*j/st;
		}
	}

	// print faces
	//glEnableClientState(GL_COLOR_ARRAY);
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D,texture[0]);
	// fails? GLuint faces[4];
	// TODO: later try a trimesh -> [(vrs_total-1)*(vr_length-1)];

	for(j=0;j<vrs_total-1;j++) {	// visit vertical range of tiles
	for(i=0;i<vr_length-1;i++) {	// visit horiz range of tiles

		glvertices[0*3+0] = vertices[(j*vr_length+i)*3+0];
		glvertices[0*3+1] = vertices[(j*vr_length+i)*3+1];
		glvertices[0*3+2] = vertices[(j*vr_length+i)*3+2];
		gltextures[0*2+0] = uv_rows[((j%st)*(st+1)+(i%st))*2+0];
		gltextures[0*2+1] = uv_rows[((j%st)*(st+1)+(i%st))*2+1];
		// faces[0]=j*vr_length+i;

		glvertices[1*3+0] = vertices[(j*vr_length+i+1)*3+0];
		glvertices[1*3+1] = vertices[(j*vr_length+i+1)*3+1];
		glvertices[1*3+2] = vertices[(j*vr_length+i+1)*3+2];
		gltextures[1*2+0] = uv_rows[((j%st)*(st+1)+(i%st+1))*2+0];
		gltextures[1*2+1] = uv_rows[((j%st)*(st+1)+(i%st+1))*2+1];
		// faces[1]=j*vr_length+i+1;

		glvertices[2*3+0] = vertices[(j*vr_length+vr_length+i)*3+0];
		glvertices[2*3+1] = vertices[(j*vr_length+vr_length+i)*3+1];
		glvertices[2*3+2] = vertices[(j*vr_length+vr_length+i)*3+2];
		gltextures[2*2+0] = uv_rows[((j%st+1)*(st+1)+(i%st))*2+0];
		gltextures[2*2+1] = uv_rows[((j%st+1)*(st+1)+(i%st))*2+1];
		// faces[2]=j*vr_length+vr_length+i+1;

		glvertices[3*3+0] = vertices[(j*vr_length+vr_length+i)*3+0+3];
		glvertices[3*3+1] = vertices[(j*vr_length+vr_length+i)*3+1+3];
		glvertices[3*3+2] = vertices[(j*vr_length+vr_length+i)*3+2+3];
		gltextures[3*2+0] = uv_rows[((j%st+1)*(st+1)+(i%st+1))*2+0];
		gltextures[3*2+1] = uv_rows[((j%st+1)*(st+1)+(i%st+1))*2+1];
		// faces[3]=j*vr_length+vr_length+i;

		//glColorPointer(4,GL_FLOAT,0,glcolors);
		glTexCoordPointer(2,GL_FLOAT,0,gltextures);
		glVertexPointer(3,GL_FLOAT,0,glvertices);
		// TODO: this fails but would be better 
		// try fix: glDrawElements(GL_TRIANGLES,1,GL_FIXED,faces);
		glDrawArrays(wireframe ? GL_LINE_LOOP : GL_TRIANGLE_STRIP,0,4);
	}
	}

	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glDisable(GL_TEXTURE_2D);
}

void globe_redraw() {
  glClearColor(0, 0, 0, 0);            
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  glLoadIdentity();
  float scale = pow(2,zoom);
  glTranslatef(0,0,-(scale+24+surface));
  //glTranslatef(0,0,-(4+surface));
  glScalef(scale,scale,scale);

  if(world_matrix) {
    glMultMatrixf(world_matrix);
    longitude = atan2(world_matrix[2],world_matrix[10])*180.0/3.14159;
    latitude = -asin(world_matrix[6])*180.0/3.14159;
    //fprintf(stdout,"lat lon %f %f\n",latitude,longitude);
  }
  int temp = zoom;
  zoom = 0;
  wireframe = 1;
  globe_print();
  zoom = temp;
  wireframe = 0;
  globe_print();
}

////////////////////////////////////////////////////////////////////////////////
// utility method to build a single texture
// this is just a stand in for the real tiling solution
////////////////////////////////////////////////////////////////////////////////

void BuildGLTextures() {
    int w = 256;
    int h = 128;
    char* data = (char*)texture_earth;
    glEnable(GL_TEXTURE_2D);
    glGenTextures(1,&texture[0]);
    glBindTexture(GL_TEXTURE_2D,texture[0]);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
    //glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    //glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,w,h,0,GL_RGB,GL_UNSIGNED_BYTE,data);
};


////////////////////////////////////////////////////////////////////////////////
//
// setup wms url
//
////////////////////////////////////////////////////////////////////////////////

/*
public function WMSLayer(wmsurl, is_flat:Boolean=0,scale=1000,zoom=0):void {
		this.wmsurl = wmsurl;
		if(!this.wmsurl) {
		//this.wmsurl = "http://civicmaps.org/tom/tilecache-1.4/tilecache.cgi?LAYERS=basic&FORMAT=image%2Fpng&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&SRS=EPSG%3A4326"
		this.wmsurl = "http://civicmaps.org/cgi-bin/mapserv?map=/www/sites/maps.civicactions.net/maps/world.map&service=WMS&WMTVER=1.0.0&REQUEST=map&SRS=EPSG:4326&LAYERS=bluemarble,landsat7,lakes,rivers,cities,majorroads,minorroads,tiger_polygon,tiger_landmarks,tiger_lakes,tiger_local_roads,tiger_major_roads,lowboundaries,boundaries,coastlines&FORMAT=image/jpeg&STYLES=&TRANSPARENT=TRUE"
		}
		this.is_flat = is_flat;
		super( null, new Array(), new Array(), null, null );
		rebuild(is_flat,scale,zoom);
	}
	}
}
*/

////////////////////////////////////////////////////////////////////////////////
//
// opengles initialization for iphone and mobile devices
//
////////////////////////////////////////////////////////////////////////////////

#ifdef MOBILE

int InitializeEGL() {
    EGLDisplay eglDisplay = eglGetDisplay(0);   
    EGLint versMajor;
    EGLint versMinor;
    EGLBoolean result = eglInitialize(eglDisplay, &versMajor, &versMinor);
    if (result) {
        fprintf(stdout,"GL version %d.%d\n", versMajor, versMinor);
        
        EGLint numConfigs;
        
        if (eglGetConfigs(eglDisplay, 0, 0, &numConfigs))
        {
            fprintf(stdout,"%d egl configs\n", numConfigs);
            
            EGLConfig configs[32];
            eglGetConfigs(eglDisplay, configs, 32, &numConfigs);
            
            int i;
            for (i = 0; i < numConfigs; i++)
            {
                EGLint redBits;
                EGLint greenBits;
                EGLint blueBits;
                EGLint alphaBits;
                
                EGLint bufferSize;
                EGLint depthSize;
                EGLint stencilSize;
                
                EGLint caveat;
                EGLint native;
                
                EGLint level;
                EGLint surfaceType;
                EGLint visualType;
                EGLint visualID;
                
                eglGetConfigAttrib(eglDisplay, configs[i], EGL_RED_SIZE,    &redBits);
                eglGetConfigAttrib(eglDisplay, configs[i], EGL_GREEN_SIZE,  &greenBits);
                eglGetConfigAttrib(eglDisplay, configs[i], EGL_BLUE_SIZE,   &blueBits);
                eglGetConfigAttrib(eglDisplay, configs[i], EGL_ALPHA_SIZE,  &alphaBits);

                eglGetConfigAttrib(eglDisplay, configs[i], EGL_BUFFER_SIZE, &bufferSize);
                eglGetConfigAttrib(eglDisplay, configs[i], EGL_DEPTH_SIZE,  &depthSize);
                eglGetConfigAttrib(eglDisplay, configs[i], EGL_STENCIL_SIZE, &stencilSize);
                eglGetConfigAttrib(eglDisplay, configs[i], EGL_CONFIG_CAVEAT, &caveat);
                eglGetConfigAttrib(eglDisplay, configs[i], EGL_NATIVE_RENDERABLE, &native);

                eglGetConfigAttrib(eglDisplay, configs[i], EGL_LEVEL, &level);
                eglGetConfigAttrib(eglDisplay, configs[i], EGL_SURFACE_TYPE, &surfaceType);
                eglGetConfigAttrib(eglDisplay, configs[i], EGL_NATIVE_VISUAL_TYPE, &visualType);
                eglGetConfigAttrib(eglDisplay, configs[i], EGL_NATIVE_VISUAL_ID, &visualID);

                printf(
                     "config %d: rgba=%d:%d:%d:%d(%d), ds=%d:%d, "
                     "caveat=%04x, native=%d, level=%d, surface=%04x, visual=%04x, id=%d\n",
                     i + 1, 
                     redBits, greenBits, blueBits, alphaBits, bufferSize, depthSize, stencilSize,
                     caveat, native, level, surfaceType, visualType, visualID
                 );
            }
            
            EGLint configAttribs[] = {
                EGL_BUFFER_SIZE, 16,
                EGL_DEPTH_SIZE, 16,
                EGL_SURFACE_TYPE, EGL_WINDOW_BIT, // EGL_WINDOW_BIT, EGL_PIXMAP_BIT,
                EGL_NONE
            };
            
            EGLConfig eglConfig = 0;
            if (!eglChooseConfig(eglDisplay, configAttribs, &eglConfig, 1, &numConfigs) || (numConfigs != 1))
            {
                fprintf(stdout,"failed to find usable config =(\n");
            }
            else
            {
                GLint configID;
                eglGetConfigAttrib(eglDisplay, eglConfig, EGL_CONFIG_ID, &configID);
                fprintf(stdout,"chose config %d\n", configID);
            }
            
            EGLContext eglContext = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, 0);
            
            if (eglContext == EGL_NO_CONTEXT)
                fprintf(stdout,"failed to allocate context =(\n");
            
            EGLSurface eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, 0, 0);
            if (eglSurface == EGL_NO_SURFACE)
                fprintf(stdout,"failed to create surface =(\n");
            
            return eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
        }
        else
            fprintf(stdout,"GL get configs failed\n");
    }
    else
        fprintf(stdout,"GL init failed\n");
    
    return 0;
}

void ShutdownEGL() {
    eglMakeCurrent(eglGetCurrentDisplay(), 
			EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglTerminate(eglGetCurrentDisplay());
}

#endif

////////////////////////////////////////////////////////////////////////////////
//
// mobile version boot strapping
//
////////////////////////////////////////////////////////////////////////////////

#ifdef MOBILE

void globe_start(int w,int h) {
    if (!InitializeEGL()) {
        return;
    }
    BuildGLTextures();
    ArcBall_initialize(w,h);
    initialized = 1;
}

void globe_event(int event,int x,int y) {
    if(initialized) {
        world_matrix = ArcBall_update(x,y,
                      (event == GLOBE_MOUSEDRAG || event == GLOBE_MOUSEDOWN )
                            ? 1 : 0);
        globe_redraw();
        eglSwapBuffers(eglGetCurrentDisplay(),eglGetCurrentSurface(EGL_DRAW));
        //usleep(33);
    }
}

void globe_stop() {
    if(initialized) {
      ShutdownEGL();
      // CGDisplayRelease(mainDisplay);
    }
}

#endif

////////////////////////////////////////////////////////////////////////////////
//
// native image handling
//
////////////////////////////////////////////////////////////////////////////////

#ifndef MOBILE

struct Image {
    unsigned long sizeX;
    unsigned long sizeY;
    char *data;
};
typedef struct Image Image;

unsigned int LongSwap (unsigned int i) {
	unsigned char b1, b2, b3, b4;
	b1 = i & 255;
	b2 = ( i >> 8 ) & 255;
	b3 = ( i>>16 ) & 255;
	b4 = ( i>>24 ) & 255;
	return ((int)b1 << 24) + ((int)b2 << 16) + ((int)b3 << 8) + b4;
}

// quick and dirty bitmap loader...for 24 bit bitmaps with 1 plane only.  
int ImageLoad(char *filename, Image *image) {
    FILE *file;
    unsigned long size;
    unsigned long i;
    unsigned short int planes;          // number of planes in image (must be 1) 
    unsigned short int bpp;             // number of bits per pixel (must be 24)
    char temp;
    if ((file = fopen(filename, "rb"))==NULL) {
		fprintf(stdout,"File Not Found : %s\n",filename);
		return 0;
    }
    fseek(file, 18, SEEK_CUR);
    if ((i = fread(&image->sizeX, 4, 1, file)) != 1) {
		fprintf(stdout,"Error reading width from %s.\n", filename);
		return 0;
    }
    fprintf(stdout,"Width of %s: %lu\n", filename, image->sizeX);
    // read the height 
    if ((i = fread(&image->sizeY, 4, 1, file)) != 1) {
		fprintf(stdout,"Error reading height from %s.\n", filename);
		return 0;
    }
    fprintf(stdout,"Height of %s: %lu\n", filename, image->sizeY);
    size = image->sizeX * image->sizeY * 3;
    // read the planes
    if ((fread(&planes, 2, 1, file)) != 1) {
		fprintf(stdout,"Error reading planes from %s.\n", filename);
		return 0;
    }
    if (planes != 1) {
		fprintf(stdout,"Planes from %s is not 1: %u\n", filename, planes);
		return 0;
    }
    // read the bpp
    if ((i = fread(&bpp, 2, 1, file)) != 1) {
		fprintf(stdout,"Error reading bpp from %s.\n", filename);
		return 0;
    }
    if (bpp != 24) {
		fprintf(stdout,"Bpp from %s is not 24: %u\n", filename, bpp);
		return 0;
    }
    // seek past the rest of the bitmap header.
    fseek(file, 24, SEEK_CUR);
    // read the data. 
    image->data = (char *) malloc(size);
    if (image->data == NULL) {
		fprintf(stdout,"Error allocating memory for color-corrected image data");
		return 0;	
    }
    if ((i = fread(image->data, size, 1, file)) != 1) {
		fprintf(stdout,"Error reading image data from %s.\n", filename);
		return 0;
    }
    for (i=0;i<size;i+=3) { // reverse all of the colors. (bgr -> rgb)
		temp = image->data[i];
		image->data[i] = image->data[i+2];
		image->data[i+2] = temp;
    }
    return 1;
}

// load an rgb file
int ImageLoadRGB(char *filename, Image *image,int w,int h) {
    FILE *file;
    unsigned long size;
    unsigned long i;
    if ((file = fopen(filename, "rb"))==NULL) {
	  fprintf(stdout,"File Not Found : %s\n",filename);
	  return 0;
    }
    image->sizeX = w;
    image->sizeY = h;
    size = image->sizeX * image->sizeY * 3;
    image->data = (char *) malloc(size);
    if (image->data == NULL) {
	  fprintf(stdout,"Error allocating memory for color-corrected image data");
	  return 0;	
    }
    if ((i = fread(image->data, size, 1, file)) != 1) {
	  fprintf(stdout,"Error reading image data from %s.\n", filename);
	  return 0;
    }
    return 1;
}
    
// Load Bitmaps And Convert To Textures
void LoadGLTextures() {	
    Image *image1;
    glEnable(GL_TEXTURE_2D);
    image1 = (Image *) malloc(sizeof(Image)); // not sure what the point of this idiocy is
    if (image1 == NULL) {
	  fprintf(stdout,"Error allocating space for image");
    }
    if (!ImageLoadRGB("earth.rgb", image1,256,128)) {
	  return;
    }
    glGenTextures(1, &texture[0]);
    glBindTexture(GL_TEXTURE_2D, texture[0]);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); 
    glTexImage2D(GL_TEXTURE_2D, 0, 3, image1->sizeX, image1->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, image1->data);
};

////////////////////////////////////////////////////////////////////////////////
//
// native boot strap
//
////////////////////////////////////////////////////////////////////////////////

int window;
int mState = 0;

// keyboard controls
void keyPressed(unsigned char key, int x, int y)  {
	usleep(100);
	if (key == ESCAPE) { 
	  glutDestroyWindow(window);
	}
	if (key == 45) { if(zoom>0)zoom--; } // zoom out with minus key
	if (key == 43 || key==61) { if(zoom<10)zoom++; } // zoom with plus key
	//if (key == 52) { longitude-=5;if(longitude<-180)longitude=180; } 
	//if (key == 54) { longitude+=5;if(longitude>180)longitude=-180; }
	//if (key == 56) { latitude+=5;if(latitude>90)latitude=-90; } 
	//if (key == 50) { latitude-=5;if(latitude<-90)latitude=90; }

	fprintf(stdout,"z,lon,lat=%d,%f %f %f\n",key, zoom,longitude,latitude);
	fflush(stdout);
}

// mouse event
void glutMotion(int x, int y) {
   world_matrix = ArcBall_update(x,y,mState);
}

// mouse event
void glutMouse(int button, int state, int x, int y) {
    if(state == GLUT_DOWN) {
      switch(button) {
        case GLUT_LEFT_BUTTON:
        case GLUT_RIGHT_BUTTON:
          mState = 1;
   		  world_matrix = ArcBall_update(x,y,mState);
        }
    } else if (state == GLUT_UP) {
        mState = 0;
	    world_matrix = ArcBall_update(x,y,mState);
    }
}

// resize 
void ReSizeGLScene(int Width, int Height) {
  if (Height==0) Height=1;
  glViewport(0, 0, Width, Height);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f);
  glMatrixMode(GL_MODELVIEW);
}

// draw
void DrawGLScene() {
  globe_redraw();
  glutSwapBuffers();
  usleep(10);
}

int main(int argc, char **argv)  {
  glutInit(&argc, argv);  
  glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH);  
  glutInitWindowSize(WIDTH,HEIGHT);  
  glutInitWindowPosition(0, 0);  
  window = glutCreateWindow("Globe");
  glutDisplayFunc(&DrawGLScene);  
  //glutFullScreen();
  glutIdleFunc(&DrawGLScene);
  glutReshapeFunc(&ReSizeGLScene);
  glutKeyboardFunc(&keyPressed);
  glutMotionFunc (glutMotion);
  glutMouseFunc (glutMouse);

  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  glClearDepth(1.0);
  glDepthFunc(GL_LESS);
  glEnable(GL_DEPTH_TEST);
  glShadeModel(GL_SMOOTH);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(45.0f,(GLfloat)WIDTH/(GLfloat)HEIGHT,0.1f,100.0f);
  glMatrixMode(GL_MODELVIEW);

  BuildGLTextures();
  ArcBall_initialize(WIDTH,HEIGHT);
  initialized = 1;
  glutMainLoop();
  return 1;
}

#endif

